package com.example.scribe;

import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;

import com.example.scribe.error.ScribeError;

/**
 * Utility to parse input using the generated ANTLR parser and convert the parse tree
 * into the project's `SyntaxNode` representation.
 * 
 * This class handles parsing and error handling, while delegating the actual
 * conversion logic to {@link ParseTreeConverter}.
 * 
 * Uses reflection to access generated ANTLR classes to avoid lint errors.
 */
public class ParserUtil {
    /**
     * Parse input string and convert to SyntaxNode AST.
     * 
     * @param input The Scribe source code to parse
     * @return The root SyntaxNode of the parsed AST
     * @throws ScribeError if parsing fails
     */
    public static SyntaxNode parseToSyntax(String input) {
        try {
            // Use reflection to instantiate lexer and parser to avoid lint errors
            // Use the current class's class loader to ensure we can find generated classes
            ClassLoader classLoader = ParserUtil.class.getClassLoader();
            Class<?> lexerClass = Class.forName("com.example.scribe.parser.ScribeLexer", true, classLoader);
            Class<?> parserClass = Class.forName("com.example.scribe.parser.ScribeParser", true, classLoader);
            
            // Create lexer: new ScribeLexer(CharStreams.fromString(input))
            Object lexer = lexerClass.getConstructor(
                org.antlr.v4.runtime.CharStream.class
            ).newInstance(CharStreams.fromString(input));
            
            // Create token stream: new CommonTokenStream(lexer)
            CommonTokenStream tokens = new CommonTokenStream(
                (org.antlr.v4.runtime.Lexer) lexer
            );
            
            // Create parser: new ScribeParser(tokens)
            // ANTLR parser constructor takes TokenStream, not CommonTokenStream
            Object parser = parserClass.getConstructor(
                org.antlr.v4.runtime.TokenStream.class
            ).newInstance(tokens);
            
            // Set error listener
            parserClass.getMethod("removeErrorListeners").invoke(parser);
            parserClass.getMethod("addErrorListener", 
                org.antlr.v4.runtime.ANTLRErrorListener.class
            ).invoke(parser, new ScribeErrorListener(input));
            
            // Parse: parser.program()
            ParserRuleContext tree = (ParserRuleContext) parserClass
                .getMethod("program")
                .invoke(parser);
            
            // Check for syntax errors
            int errorCount = (Integer) parserClass
                .getMethod("getNumberOfSyntaxErrors")
                .invoke(parser);
            if (errorCount > 0) {
                throw ScribeError.syntaxError(input, 1, 0, "语法解析失败");
            }
            
            // Use ParseTreeConverter for conversion
            ParseTreeConverter converter = new ParseTreeConverter();
            return converter.convert(tree);
        } catch (ScribeError e) {
            throw e;
        } catch (ClassNotFoundException e) {
            throw ScribeError.parseError(input, 1, 0, 
                "无法找到生成的解析器类: " + e.getMessage() + 
                "。请确保已运行 Maven 编译生成 ANTLR 类。");
        } catch (NoSuchMethodException e) {
            throw ScribeError.parseError(input, 1, 0, 
                "反射调用失败，找不到方法或构造函数: " + e.getMessage());
        } catch (java.lang.reflect.InvocationTargetException e) {
            Throwable cause = e.getCause();
            if (cause instanceof ScribeError) {
                throw (ScribeError) cause;
            }
            throw ScribeError.parseError(input, 1, 0, 
                "解析器执行错误: " + (cause != null ? cause.getMessage() : e.getMessage()));
        } catch (Exception e) {
            throw ScribeError.parseError(input, 1, 0, 
                "解析错误: " + e.getClass().getSimpleName() + ": " + e.getMessage());
        }
    }
    
    /**
     * Error listener that throws ScribeError on syntax errors.
     */
    private static class ScribeErrorListener extends BaseErrorListener {
        private final String input;

        public ScribeErrorListener(String input) {
            this.input = input;
        }

        @Override
        public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
                              int line, int charPositionInLine,
                              String msg, RecognitionException e) {
            throw ScribeError.syntaxError(input, line, charPositionInLine, msg);
        }
    }
}
